import numpy as np
import numpy.ctypeslib as npC
import ctypes as C
from math import floor
import os

class Sampler(object):
        def __init__( self, ringsRadius, samplesPerRing ):
                _path = os.path.dirname(__file__)
                self._lib = npC.load_library("libsampler.so",_path)

                self._lib.Sampler_new.restype = C.c_void_p
                self._lib.Sampler_new.argtypes = [
                        C.c_uint,
                        npC.ndpointer(ndim = 1, dtype = 'uint32', flags =  'c_contiguous'),
                        npC.ndpointer(ndim = 1, dtype = 'uint32', flags =  'c_contiguous')
                ]

                self._lib.Sampler_get_point.restype = None
                self._lib.Sampler_get_point.argtypes = [
                        C.c_void_p,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous'),
                        C.c_uint,
                        C.c_uint,
                        C.c_uint,
                        C.c_uint,
                        npC.ndpointer(ndim = 1, dtype = 'float64', flags =  'c_contiguous')
                        ]

                self._lib.Sampler_get_points.restype = None
                self._lib.Sampler_get_points.argtypes = [
                        C.c_void_p,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous'),
                        C.c_uint,
                        C.c_uint,
                        npC.ndpointer(ndim = 1, dtype = 'uint32', flags =  'c_contiguous'),
                        C.c_uint,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous')
                        ]

                self._lib.Sampler_get_image.restype = None
                self._lib.Sampler_get_image.argtypes = [
                        C.c_void_p,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous'),
                        C.c_uint,
                        C.c_uint,
                        npC.ndpointer(ndim = 3, dtype = 'float64', flags =  'c_contiguous')
                        ]

                self._lib.Sampler_getXRes.restype = C.c_uint
                self._lib.Sampler_getXRes.argtypes = [
                        C.c_void_p,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous'),
                        C.c_uint,
                        C.c_uint
                        ]

                self._lib.Sampler_getYRes.restype = C.c_uint
                self._lib.Sampler_getYRes.argtypes = [
                        C.c_void_p,
                        npC.ndpointer(ndim = 2, dtype = 'float64', flags =  'c_contiguous'),
                        C.c_uint,
                        C.c_uint
                        ]

                self._lib.Sampler_nbSamples.restype = C.c_uint
                self._lib.Sampler_nbSamples.argtypes = [ C.c_void_p ]

                self._lib.Sampler_delete.restype = None
                self._lib.Sampler_delete.argtypes = [ C.c_void_p ]


                rings = len(ringsRadius)
                ringsRadius = np.array(ringsRadius,'uint32')
                samplesPerRing = np.array(samplesPerRing, 'uint32')
                self._object = self._lib.Sampler_new( rings, ringsRadius, samplesPerRing )

        def __del__(self):
                self._lib.Sampler_delete(self._object)

        def __call__( self, data, xyList=None, y=None ):
                ## Allows calls like :
                ##      mySampler( myImageData )                                # Whole imake
                ##      mySampler( myImageData, x, y )                          # Single point
                ##      mySampler( myImageData, [x, y] )                        # Single point
                ##      mySampler( myImageData, [x1, y1, x2, y2, ... ,xn, yn] ) # Multiple points

                if(xyList is not None):
                        if(y is not None):
                                res = np.zeros( self.nbSamples() , 'float64' )
                                self._lib.Sampler_get_point( self._object, data, data.shape[0], data.shape[1], xyList, y, res )
                        elif( len(xyList) == 2 ):
                                res = np.zeros( self.nbSamples() , 'float64' )
                                self._lib.Sampler_get_point( self._object, data, data.shape[0], data.shape[1], xyList[0], xyList[1], res )
                        else:
                                xyList=np.array(xyList,'uint32')
                                res = np.zeros( (int(floor(xyList.shape[0]/2.0)), self.nbSamples()) , 'float64' )
                                self._lib.Sampler_get_points( self._object, data, data.shape[0], data.shape[1], xyList, int(floor(xyList.shape[0]/2.0)), res )
                else:
                        res = np.zeros( (self.getXRes(data), self.getYRes(data), self.nbSamples()) , 'float64' )
                        self._lib.Sampler_get_image( self._object, data, data.shape[0], data.shape[1], res )
                return res

        def getXRes( self, data ):
                return self._lib.Sampler_getXRes( self._object, data, data.shape[0], data.shape[1] )

        def getYRes( self, data ):
                return self._lib.Sampler_getYRes( self._object, data, data.shape[0], data.shape[1] )

        def nbSamples(self):
                return self._lib.Sampler_nbSamples( self._object )
